﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ExcelUtilities.Data;

namespace ExcelUtilities
{
    /// <summary>
    /// 将处理在读取之前触发的事件的函数
    /// </summary>
    /// <param name="workbook">配置信息</param>
    public delegate void BeforeReadHandler(Cfg.Workbook workbook);
    /// <summary>
    /// 将处理在读取完成后触发的事件的函数
    /// </summary>
    /// <param name="workbookData">读取结果</param>
    public delegate void AfterReadHandler(Data.WorkbookData workbookData);

    /// <summary>
    /// excel reader
    /// </summary>
    public abstract class ExcelReader
    {
        /// <summary>
        /// 读取excel文件
        /// </summary>
        /// <param name="cfgFile">配置文件</param>
        /// <param name="name">配置项名称
        /// 对应Workbook节的 Name属性
        /// </param>
        /// <param name="xlsFile">将要读取的excel文件</param>
        /// <returns></returns>
        public WorkbookData Read(string cfgFile, string name, string xlsFile)
        {
            System.IO.FileStream stream = new System.IO.FileStream(xlsFile, System.IO.FileMode.Open, System.IO.FileAccess.Read);
            var workbookData = Read(cfgFile, name, stream);
            stream.Close();

            return workbookData;
        }
        /// <summary>
        /// 读取excel文件
        /// </summary>
        /// <param name="cfgFile">配置文件</param>
        /// <param name="name">配置项名称
        /// 对应Workbook节的 Name属性
        /// </param>
        /// <param name="stream">excel数据流</param>
        /// <returns></returns>
        public WorkbookData Read(string cfgFile, string name, System.IO.Stream stream)
        {
            //读取配置
            var workbookCfg = Cfg.CfgReader.Get(cfgFile, name);
            var validateError = "";

            Open(stream);

            if (OnBeforeRead != null)
                OnBeforeRead(workbookCfg);

            //获取单元格的值的函数
            Func<Cfg.Worksheet, int, int, CellValue> getValue = delegate(Cfg.Worksheet workSheet, int row, int col)
            {
                if (string.IsNullOrEmpty(workSheet.SheetName))
                    return new CellValue(GetCellValue(workSheet.Order, row, col));
                else
                    return new CellValue(GetCellValue(workSheet.SheetName, row, col));
            };
            //检测单元格值是否匹配的函数
            Func<string, Cfg.Worksheet, int, int, bool> matchTest = delegate(string pattern, Cfg.Worksheet workSheet, int row, int col)
            {
                string value = getValue(workSheet, row, col);
                return (new System.Text.RegularExpressions.Regex(pattern, System.Text.RegularExpressions.RegexOptions.ECMAScript)
                    .IsMatch(value));
            };

            WorkbookData data = new WorkbookData();
            //读取excel
            foreach (var workSheet in workbookCfg.Worksheets)
            {
                WorksheetData sheetData = new WorksheetData();
                CellValue value;
                //读取单元格的值
                foreach (var cell in workSheet.Cells)
                {
                    if (string.IsNullOrEmpty(workSheet.SheetName))
                    {
                        MakeCellAddr(workSheet.Order, cell);
                        value = new CellValue(GetCellValue(workSheet.Order, cell.Address));
                    }
                    else
                    {
                        MakeCellAddr(workSheet.SheetName, cell);
                        value = new CellValue(GetCellValue(workSheet.SheetName, cell.Address));
                    }

                    if (!string.IsNullOrEmpty((validateError = Validate(string.Format("{0}.{1}", workSheet.Name, cell.Address), value, cell.DataType, cell.ErrorMessage))))
                    {
                        goto H;
                    }
                    sheetData.CellDatas.Add(cell.Name, value);
                }
                //读取表格数据 
                foreach (var table in workSheet.Ranges)
                {
                    var tableData = new TableData();
                    tableData.Name = table.Name;
                    int endRow;
                    if (table.EndRow > 0)
                    {
                        endRow = table.EndRow;
                    }
                    else
                    {
                        if (string.IsNullOrEmpty(workSheet.SheetName))
                            endRow = GetEndPos(workSheet.Order).Row;
                        else
                            endRow = GetEndPos(workSheet.SheetName).Row;
                        endRow += table.EndRow;
                    }

                    bool readBegin = !table.Columns.Any(a => !string.IsNullOrEmpty(a.StartMark));

                    for (int row = table.StartRow; row <= endRow; row++)
                    {
                        // 开始行检测
                        if (!readBegin)
                        {
                            readBegin = !table.Columns.Any(a => !string.IsNullOrEmpty(a.StartMark)
                                && !matchTest(a.StartMark, workSheet, row, a.Order));

                            continue;
                        }
                        //结束行检测 
                        if (table.Columns.Any(a => !string.IsNullOrEmpty(a.EndMark)
                            && matchTest(a.EndMark, workSheet, row, a.Order)))
                        {
                            break;
                        }

                        //内容匹配检测
                        bool isMatch = !table.Columns.Any(a => !string.IsNullOrEmpty(a.Mark)
                            && !matchTest(a.Mark, workSheet, row, a.Order));

                        if (isMatch)
                        {
                            var rowData = new Dictionary<string, CellValue>();
                            foreach (var column in table.Columns)
                            {
                                value = getValue(workSheet, row, column.Order);
                                if (!string.IsNullOrEmpty((validateError = Validate(string.Format("{0}.{1}", workSheet.Name, column.Address), value, column.DataType, column.ErrorMessage))))
                                {
                                    goto H;
                                }
                                rowData.Add(column.Name, value);
                            }
                            tableData.RowDatas.Add(rowData);
                        }
                    }
                    sheetData.TableDatas.Add(tableData);
                }
                data.SheetDatas.Add(sheetData);
            }
        H:
            //关闭
            Close();
            //stream.Close(); //关闭流

            if (!string.IsNullOrEmpty(validateError))
            {
                throw new Exceptions.DataTypeException(validateError);
            }

            if (OnAfterRead != null)
                OnAfterRead(data);

            return data;
        }

        #region

        /// <summary>
        /// 打开文件
        /// </summary>
        /// <param name="xlsFile">要打开的execl文件</param>
        protected abstract void Open(string xlsFile);
        /// <summary>
        /// 打开文件
        /// </summary>
        /// <param name="stream">execl数据流</param>
        protected abstract void Open(System.IO.Stream stream);
        /// <summary>
        /// 关闭，读完成后调用
        /// </summary>
        protected abstract void Close();
                
        /// <summary>
        /// 获取最后一行的行号
        /// </summary>
        /// <param name="sheetOrder">第几个工作表（从1开始）</param>
        /// <returns>返回最后一行行号（从1开始）</returns>
        protected abstract Cfg.CellPosition GetEndPos(int sheetOrder);
        /// <summary>
        /// 获取最后一行的行号
        /// </summary>
        /// <param name="sheetName">工作表表名</param>
        /// <returns>返回最后一行行号（从1开始）</returns>
        protected abstract Cfg.CellPosition GetEndPos(string sheetName);

        /// <summary>
        /// 获取单元格的值
        /// </summary>
        /// <param name="sheetOrder">第几个工作表（从1开始）</param>
        /// <param name="cellAddress">单元格地址</param>
        /// <returns>返回单元格的值</returns>
        protected abstract object GetCellValue(int sheetOrder, string cellAddress);
        /// <summary>
        /// 获取单元格的值
        /// </summary>
        /// <param name="sheetOrder">第几个工作表（从1开始）</param>
        /// <param name="row">单元格在第几行(从1开始)</param>
        /// <param name="col">单元格在第几列(从1开始)</param>
        /// <returns>返回单元格的值</returns>
        protected abstract object GetCellValue(int sheetOrder, int row, int col);
        /// <summary>
        /// 获取单元格的值
        /// </summary>
        /// <param name="sheetName">工作表名</param>
        /// <param name="cellAddress">单元格地址</param>
        /// <returns>返回单元格的值</returns>
        protected abstract object GetCellValue(string sheetName, string cellAddress);
        /// <summary>
        /// 获取单元格的值
        /// </summary>
        /// <param name="sheetName">工作表名</param>
        /// <param name="row">单元格在第几行(从1开始)</param>
        /// <param name="col">单元格在第几列(从1开始)</param>
        /// <returns>返回单元格的值</returns>
        protected abstract object GetCellValue(string sheetName, int row, int col);

        /// <summary>
        /// 在excel读取之前触发该事件
        /// </summary>
        public event BeforeReadHandler OnBeforeRead;
        /// <summary>
        /// 在excel读取完成时触发该事件
        /// </summary>
        public event AfterReadHandler OnAfterRead;

        #endregion

        #region

        private void MakeCellAddr(int sheetOrder, Cfg.Cell cell)
        {
            if (string.IsNullOrEmpty(cell.Address))
            {
                Cfg.CellPosition cellPos = new Cfg.CellPosition() { Row = cell.Row, Col = cell.Col };
                var endPos = GetEndPos(sheetOrder);
                if (cellPos.Row < 0)
                    cellPos.Row = endPos.Row + cellPos.Row + 1;
                if (cellPos.Col < 0)
                    cellPos.Col = endPos.Col + cellPos.Col + 1;
                cell.Address = ExcelUtilities.Cfg.AddressExtensions.ToAddr(cellPos);
            }
        }
        private void MakeCellAddr(string sheetName, Cfg.Cell cell)
        {
            if (string.IsNullOrEmpty(cell.Address))
            {
                Cfg.CellPosition cellPos = new Cfg.CellPosition() { Row = cell.Row, Col = cell.Col };
                var endPos = GetEndPos(sheetName);
                if (cellPos.Row < 0)
                    cellPos.Row = endPos.Row + cellPos.Row + 1;
                if (cellPos.Col < 0)
                    cellPos.Col = endPos.Col + cellPos.Col + 1;
                cell.Address = ExcelUtilities.Cfg.AddressExtensions.ToAddr(cellPos);
            }
        }

        /// <summary>
        /// 验证单元格的数据是否之前
        /// </summary>
        /// <param name="cellAddress">单元格地址</param>
        /// <param name="value">数据值</param>
        /// <param name="rule">规则</param>
        /// <returns>校验不通过是返回错误信息，否则返回空字符串</returns>
        private string Validate(string cellAddress, CellValue value, string rule, string errorMessage)
        {
            var validator = new Validator(cellAddress, value, rule, errorMessage);

            return validator.Execute();
        }

        #endregion
    }
}